package com.flyqiu.flow.bpmn;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.flyqiu.common.tools.FlyQiuCollectionTools;
import com.flyqiu.common.tools.StringTools;
import com.flyqiu.flow.bpmn.element.BaseProcElement;
import com.flyqiu.flow.bpmn.element.IProcElement;
import com.flyqiu.flow.bpmn.element.ProcessModel;
import com.flyqiu.flow.bpmn.element.SubProcessModel;
import lombok.Data;

import java.util.*;
import java.util.function.Function;

/**
 * BPM 模型处理器，用于管理和操作流程模型。
 */
@Data
public class FlyQiuBpmModeler {

    /**
     * 主流程定义键。
     */
    public static final String PROC_MAIN_DEF_KEY = "defKey";

    /**
     * 主流程定义名称。
     */
    public static final String PROC_MAIN_DEF_NAME = "defName";

    /**
     * 存储所有流程模型的列表。
     */
    private List<ProcessModel> processModels;

    /**
     * 流程定义键。
     */
    private String defKey;

    /**
     * 流程定义名称。
     */
    private String defName;

    /**
     * 流程图 JSON 数组。
     */
    private JSONArray diagrams;

    /**
     * BPMN JSON 对象。
     */
    private JSONObject bpmn;

    /**
     * 获取指定流程模型。
     *
     * @param modelDefKey 流程模型定义键
     * @return ProcessModel 对象，如果未找到则返回 null
     */
    public ProcessModel getProcess(String modelDefKey) {
        if (StringTools.isEmpty(modelDefKey)) return null;
        List<ProcessModel> rootProcess = processModels.stream()
                .filter(e -> e.getDefKey().equals(modelDefKey))
                .toList();
        if (!rootProcess.isEmpty()) {
            return rootProcess.get(0);
        }
        return getSubProcess(modelDefKey);
    }

    /**
     * 获取指定子流程模型。
     *
     * @param subModelDefKey 子流程模型定义键
     * @return SubProcessModel 对象，如果未找到则返回 null
     */
    public SubProcessModel getSubProcess(String subModelDefKey) {
        if (StringTools.isEmpty(subModelDefKey)) return null;
        Optional<BaseProcElement> first = processModels.stream()
                .flatMap(e -> e.getElements().stream())
                .filter(e -> e instanceof SubProcessModel && e.getDefKey().equals(subModelDefKey))
                .findFirst();
        return (SubProcessModel) first.orElse(null);
    }

    /**
     * 获取指定流程元素。
     *
     * @param curTaskKey 当前任务键
     * @return BaseProcElement 对象，如果未找到则返回 null
     */
    public BaseProcElement getFlowElement(String curTaskKey) {
        for (ProcessModel processModel : processModels) {
            BaseProcElement element = processModel.getElement(curTaskKey);
            if (element != null) return element;
        }
        return null;
    }

    /**
     * 获取所有流程元素。
     *
     * @return 所有流程元素的列表
     */
    public List<BaseProcElement> getAllElement() {
        List<BaseProcElement> result = new ArrayList<>();
        List<ProcessModel> processModels = getProcessModels();
        for (ProcessModel model : processModels) {
            List<BaseProcElement> elements = model.getElements();
            result.add(model);
            result.addAll(elements);
        }
        return result;
    }

    /**
     * 获取所有指定类型的流程元素。
     *
     * @param elementType 元素类型
     * @param clazz       元素类类型
     * @param <T>         元素类型
     * @return 指定类型的流程元素列表
     */
    public <T extends BaseProcElement> List<T> getAllElements(String elementType, Class<T> clazz) {
        return getAllElements(clazz, elementType);
    }

    /**
     * 获取所有指定类型的流程元素。
     *
     * @param clazz        元素类类型
     * @param elementTypes 元素类型集合
     * @param <T>          元素类型
     * @return 指定类型的流程元素列表
     */
    public <T extends BaseProcElement> List<T> getAllElements(Class<T> clazz, String... elementTypes) {
        Set<String> _elementType = new HashSet<>();
        FlyQiuCollectionTools.iterator(elementTypes, (item) -> {
            if (StringUtils.isBlank(item)) {
                return;
            }
            _elementType.add(item);
        });
        return getAllElements(clazz, _elementType);
    }

    /**
     * 获取所有指定类型的流程元素。
     *
     * @param clazz        元素类类型
     * @param elementTypes 元素类型集合
     * @param <T>          元素类型
     * @return 指定类型的流程元素列表
     */
    public <T extends BaseProcElement> List<T> getAllElements(Class<T> clazz, Collection<String> elementTypes) {
        Set<String> _elementType = new HashSet<>();
        FlyQiuCollectionTools.iterator(elementTypes, (item) -> {
            if (StringUtils.isBlank(item)) {
                return;
            }
            _elementType.add(item);
        });
        Function<IProcElement, Boolean> filter = procElement -> _elementType.contains(procElement.getElementType());
        return getAllElements(clazz, filter);
    }

    /**
     * 获取所有符合条件的流程元素。
     *
     * @param clazz  元素类类型
     * @param filter 过滤条件
     * @param <T>    元素类型
     * @return 符合条件的流程元素列表
     */
    public <T extends BaseProcElement> List<T> getAllElements(Class<T> clazz, Function<IProcElement, Boolean> filter) {
        List<T> result = new ArrayList<>();
        List<ProcessModel> processModels = getProcessModels();
        for (ProcessModel model : processModels) {
            result.addAll(model.getFlowElements(clazz, filter));
        }
        return result;
    }

    /**
     * 获取主流程模型。
     *
     * @return 主流程模型对象，如果未找到则返回 null
     */
    public ProcessModel getMainProcess() {
        List<ProcessModel> collect = processModels.stream()
                .filter(e -> e.getDefKey().equals(defKey))
                .toList();
        if (collect.isEmpty()) return null;
        return collect.get(0);
    }
}
